package org.yzh.scheduler.service;

import org.quartz.SchedulerException;
import org.sample.model.exception.APIException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.yzh.scheduler.mapper.JobLogMapper;
import org.yzh.scheduler.mapper.ScheduleJobMapper;
import org.yzh.scheduler.model.entity.JobLogDO;
import org.yzh.scheduler.model.entity.ScheduleJobDO;
import org.yzh.scheduler.model.enums.ResultCodes;
import org.yzh.scheduler.model.vo.Option;
import org.yzh.scheduler.schedule.SchedulerUtils;

import java.time.LocalDateTime;
import java.util.*;

@Service
public class ScheduleJobService {

    @Autowired
    private ScheduleJobMapper scheduleJobMapper;

    @Autowired
    private JobLogMapper jobLogMapper;

    @Bean
    private ApplicationRunner applicationRunner() {
        return applicationArguments -> initialJob();
    }

    public void initialJob() throws SchedulerException {
        List<ScheduleJobDO> jobs = scheduleJobMapper.find(null, null, false);
        if (jobs.isEmpty())
            return;

        for (ScheduleJobDO job : jobs) {

            if (SchedulerUtils.checkExists(job.getJobName(), job.getJobGroup()))
                SchedulerUtils.updateCron(job);
            else
                SchedulerUtils.createJob(job);
        }
    }

    public List<Option> groupNames() {
        String[] groupNames = scheduleJobMapper.groupNames();
        List<Option> result = new ArrayList<>(groupNames.length);
        for (int i = 0; i < groupNames.length; i++) {
            String groupName = groupNames[i];
            result.add(new Option(groupName, groupName));
        }
        return result;
    }

    public List<ScheduleJobDO> find(String name, String group, Boolean disable) throws SchedulerException {
        List<ScheduleJobDO> jobs = scheduleJobMapper.find(name, group, disable);

        for (ScheduleJobDO t : jobs) {
            t.setJobLog(jobLogMapper.getLately(t.getJobName(), t.getJobGroup()));
            SchedulerUtils.fillJobInfo(t);
        }

        Comparator<ScheduleJobDO> comparing = Comparator.comparing(job ->
                Optional.ofNullable(job)
                        .map(ScheduleJobDO::getJobLog)
                        .map(JobLogDO::getTriggerTime)
                        .orElse(LocalDateTime.MIN)
        );

        jobs.sort(comparing.reversed());
        return jobs;
    }

    public void add(ScheduleJobDO job) throws SchedulerException {
        LocalDateTime now = LocalDateTime.now();
        job.setCreateTime(now);
        job.setUpdateTime(now);
        int row = scheduleJobMapper.insert(job);
        if (row <= 0)
            throw new APIException(ResultCodes.OperationFailed);

        SchedulerUtils.createJob(job);
    }

    public void update(ScheduleJobDO job) throws SchedulerException {
        job.setUpdateTime(LocalDateTime.now());
        int row = scheduleJobMapper.update(job);
        if (row <= 0)
            throw new APIException(ResultCodes.OperationFailed);

        SchedulerUtils.updateCron(job);
    }

    public void delete(String name, String group) {
        int row = scheduleJobMapper.delete(name, group);
        if (row <= 0)
            throw new APIException(ResultCodes.OperationFailed);

        SchedulerUtils.delete(name, group);
    }

    public boolean runOnce(String name, String group) {
        return SchedulerUtils.runOnce(name, group);
    }

    public boolean pause(String name, String group) {
        return SchedulerUtils.pauseJob(name, group);
    }

    public boolean resume(String name, String group) {
        return SchedulerUtils.resumeJob(name, group);
    }
}